/*
 * SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD
 *
 * SPDX-License-Identifier: MIT
 */
/*
 * @Hardwares: M5Core + Module13.2 ODrive
 * @Platform Version: Arduino M5Stack Board Manager v2.1.3
 * @Dependent Library:
 * M5Stack@^0.4.6: https://github.com/m5stack/M5Stack
 */

#include "M5Stack.h"
#include "odrive.h"

// Description:
// This case uses the ODrive module to control the high-speed and precise
// rotation of the servo motor Press button C to calibrate (do not touch the
// motor shaft during this period), long press and short button A to control the
// motor rotation. Note: The motor parameter configuration in this case is only
// applicable to the motor model matched with the M5 Odrive kit. When driving
// other types of motors, please configure the parameters according to the motor
// used.

ODrive odrive(Serial1);
TFT_eSprite canvas = TFT_eSprite(&M5.Lcd);

void showStringCenter(const char* str, uint16_t color)
{
    canvas.fillScreen(TFT_BLACK);
    canvas.setTextColor(color);
    canvas.drawString(str, 160, 120, 4);
    canvas.pushSprite(0, 0);
}

void setup()
{
    M5.begin(true, false, true, true);
    canvas.setColorDepth(1);
    canvas.createSprite(320, 240);
    Serial1.begin(115200, SERIAL_8N1, 13, 5);

    canvas.setTextDatum(MC_DATUM);
    showStringCenter("ODrive", TFT_GREEN);
}

void loop()
{
    M5.update();
    if (M5.BtnA.wasReleased()) {
        odrive.setPosition(10);
    }

    if (M5.BtnA.wasReleasefor(800)) {
        odrive.setPosition(0);
    }

    if (M5.BtnB.wasReleased()) {
        showStringCenter("Save default config", TFT_GREEN);
        odrive.setDefaultConfig();
        odrive.reboot();
    }

    if (M5.BtnB.wasReleasefor(800)) {
        showStringCenter("Clear config", TFT_GREEN);
        odrive.eraseConfig();
    }

    if (M5.BtnC.wasReleased()) {
        showStringCenter("Motor CALIBRATION", TFT_WHITE);
        odrive.runState(odrive.AXIS_STATE_MOTOR_CALIBRATION, 10000);
        if (odrive.checkError()) {
            showStringCenter("Motor CALIBRATION", TFT_RED);
            return;
        }

        showStringCenter("Encoder CALIBRATION", TFT_WHITE);
        odrive.runState(odrive.AXIS_STATE_ENCODER_INDEX_SEARCH, 10000);
        if (odrive.checkError()) {
            showStringCenter("Encoder Failed", TFT_RED);
            return;
        }

        showStringCenter("Offset CALIBRATION", TFT_WHITE);
        odrive.runState(odrive.AXIS_STATE_ENCODER_OFFSET_CALIBRATION, 10000);
        if (odrive.checkError()) {
            showStringCenter("Offset Failed", TFT_RED);
            return;
        }

        odrive.setGain(211.0, 0.0225, 0.01125);
        odrive.setControlMode(odrive.CONTROL_MODE_POSITION_CONTROL);
        odrive.runState(odrive.AXIS_STATE_CLOSED_LOOP_CONTROL, 2000);

        showStringCenter("Finish", TFT_GREEN);
    }

    char data_show[100];
    sprintf(data_show, "%f, %d, %d\r\n", odrive.getVbusVoltage(), odrive.getEncoderShadowCount(), odrive.checkError());
    showStringCenter(data_show, TFT_WHITE);
    Serial.printf(data_show);
    delay(20);
}
